/*
 * 数据持久化模块
 *
 * 以多态的形式组织信息持久化的方式，
 * 目前支持文件和数据库两种方向
 *
 **/

#pragma once

#include "util.hh"
#include "config.hh"
#include "info.hh"

namespace cloud
{

class persist
{
public:
    using ptr = std::shared_ptr<persist>;
    using infos = std::vector<backup_info>;

    virtual ~persist() {}

public:
    virtual void store(const infos&) = 0;
    virtual infos load() = 0;
};

class file_persist : public persist
{
public:
    file_persist() 
        : _record(config::instance().record_file())
    {}

    virtual void store(const infos& infov)
    {
        util::json js;
        for (auto& info : infov)
            js.append(info.parser_to_json());

        if (!_record.exists())
            _record.create();

        _record.write(js.serialize());
    }

    virtual infos load()
    {
        util::json js;
        if (!_record.exists() || _record.size() == 0)
        {
            _record.create();
            return {};
        }

        js.deserialize(_record.read());

        infos infov;

        for (size_t i = 0; i < js.size(); ++i)
        {
            infov.push_back(backup_info(js[i]));
        }

        return infov;
    }
private:
    util::file _record;
};

class sql_persist : public persist
{
public:
    sql_persist() {}

    virtual void store(const infos& infov)
    {
        util::mysql::connection conn(config::instance().sql_host(),
                                     config::instance().sql_user(),
                                     config::instance().sql_passwd(),
                                     config::instance().sql_db(),
                                     config::instance().sql_port());

        for (auto& info : infov)
        {
            const backup_info::tuple_of_info& tp = info.parser_to_tuple();
            util::mysql::executer(conn).execute("replace into backup_info "
                                                "(ishot, fsize, atime, mtime, ctime, fpath, bpath, ppath, url) "
                                                "values ({}, {}, {}, {}, {}, '{}', '{}', '{}', '{}')",
                                                std::get<0>(tp), std::get<1>(tp), std::get<2>(tp),
                                                std::get<3>(tp), std::get<4>(tp), std::get<5>(tp),
                                                std::get<6>(tp), std::get<7>(tp), std::get<8>(tp));
        }
    }

    virtual infos load()
    {

        util::mysql::connection conn(config::instance().sql_host(), 
                                     config::instance().sql_user(), 
                                     config::instance().sql_passwd(),
                                     config::instance().sql_db(),
                                     config::instance().sql_port());

        util::mysql::executer(conn).execute("select * from backup_info");
        util::mysql::result res(conn);

        infos infov;
        backup_info::tuple_of_info tuple;

        while (res.get<backup_info::tuple_of_info>(&tuple))
        {
            infov.push_back(backup_info(tuple));
        }

        return infov;
    }
};

struct persist_factory
{
    template<class persist_type, class... Args>
    static persist::ptr create(Args&&... args)
    {
        return std::make_shared<persist_type>(std::forward<Args>(args)...);
    }
};

}
